#include <cstdlib>
#include <iostream>

#include "matrix.h"

using namespace std;

Matrix::Matrix() {
	row = 0;
	column = 0;
	
	data = new double*[row];

	for(unsigned int i = 0; i < row; i++)
		data[i] = new double[column];
	
	for(unsigned int i = 0; i < row; i++)
		for(unsigned int j = 0; j < column; j++)
			data[i][j] = 0;
}

Matrix::Matrix(const unsigned int& _row,const unsigned int& _column, const unsigned int& _type) : row(_row), column(_column) {
	data = new double*[row];

	for(unsigned int i = 0 ; i < row ; i++)
		data[i] = new double[column];
	
	for(unsigned int i = 0; i < row; i++)
		for(unsigned int j = 0; j < column; j++)
			data[i][j] = 0;
	
	switch(_type)	{
		case NORMAL_MATRIX:
			break;
		case IDENTITY_MATRIX:
			if(row != column)
				exit(1);
			for(unsigned int i = 0;i<row;i++)
				data[i][i] = 1;
			break;
	}
}

Matrix::Matrix(const Matrix& _m) {
	row = _m.row;
	column = _m.column;
	
	data = new double*[row];

	for(unsigned int i = 0; i < row; i++)
		data[i] = new double[column];
	
	for(unsigned int i = 0; i < row; i++)
		for(unsigned int j = 0; j < column; j++)
			data[i][j] = _m[i][j];
}

Matrix::~Matrix() {
	for(unsigned int i = 0; i < row; i++)
		delete[] data[i];
	delete[] data;
}

unsigned int Matrix::getRow() const {
	return row;
}

unsigned int Matrix::getColumn() const {
	return column;
}

void Matrix::print() const {
	for(unsigned int i=0 ; i<row ; i++) {
		cout << "| ";
		for(unsigned int j=0 ; j<column ; j++)
			cout << data[i][j] << "\t\t";
		cout << " |"<< endl;
	}
}

void Matrix::print2() const {
	cout << "\n\n\n matrix(";
	for(unsigned int i = 0 ; i < row ; i++) {
		cout << "[";
		for(unsigned int j = 0 ; j < column ; j++) {
			cout << data[i][j];
			if (j != column - 1) cout << ", ";
		}
		cout << "]";
		if (i != row - 1) cout << ", ";
	}
	cout << ");\n\n\n";
}

Matrix Matrix::transpose() const {
	Matrix result(column, row);
  
	for(unsigned int i = 0; i < row; i++)
		for(unsigned int j = 0; j < column; j++) 
			result[j][i] = (*this)[i][j];
	return result;
}

Matrix Matrix::diagonalInverse() const {
	Matrix result(column, row);
  
	for(unsigned int i = 0; i < row; i++)
		result[i][i] = 1.0/(*this)[i][i];
	return result;
}

Vector Matrix::toVector(unsigned int _column) const {
	Vector result(row);
  
	for(unsigned int i = 0; i < row; i++) {
		result[i] = data[i][_column];
	}
  
	return result;
}

double* Matrix::operator[](unsigned int i) const {
	return data[i];
}

Matrix operator+(const Matrix& _matrix1,const Matrix& _matrix2) {
	if( _matrix1.getRow() == _matrix2.getRow()  && _matrix1.getColumn() == _matrix2.getColumn()) {
		unsigned int row = _matrix1.getRow();
		unsigned int column = _matrix1.getColumn();
		Matrix result(row,column);
		
		for(unsigned int i=0; i<row; i++)
			for(unsigned int j=0; j<column; j++)
				result[i][j] = _matrix1[i][j] + _matrix2[i][j];
		
		return result;
	}
	else {
		exit(1);
	}
}

Matrix operator-(const Matrix& _matrix1,const Matrix & _matrix2) {
	if( _matrix1.getRow() == _matrix2.getRow()  && _matrix1.getColumn() == _matrix2.getColumn()) {
		unsigned int row = _matrix1.getRow();
		unsigned int column = _matrix1.getColumn();
		Matrix result(row,column);
		
		for(unsigned int i=0; i<row; i++)
			for(unsigned int j=0; j<column; j++)
				result[i][j] = _matrix1[i][j] - _matrix2[i][j];
		
		return result;
	}
	else {
		exit(1);
	}
}


Matrix Matrix::operator-(void) const {
	Matrix result(row,column);
	
	for(unsigned int i=0; i<row; i++)
		for(unsigned int j=0; j<column; j++)
			result[i][j] = - data[i][j];
	
	return result;
}

Matrix operator*(const Matrix & _matrix1,const Matrix & _matrix2) {
	if(_matrix1.getColumn() != _matrix2.getRow()) {
		exit(1);
	}
	unsigned int row = _matrix1.getRow();
	unsigned int column = _matrix2.getColumn();
	Matrix result(row,column);
	
	for(unsigned int i = 0; i < row; i++) {
		for(unsigned int j = 0; j < column; j++) {
			result[i][j] = 0;
			for(unsigned int k = 0; k < _matrix1.getColumn(); k++)
				result[i][j] = result[i][j] + _matrix1[i][k] * _matrix2[k][j];
		}
	}
	return result;
}

Matrix& Matrix::operator= (const Matrix& _m) {
	for(unsigned int i = 0; i < row; i++)
		delete[] data[i];
	delete[] data;
	
	row = _m.getRow();
	column = _m.getColumn();
	
	data = new double*[row];
	
	for(unsigned int i = 0; i < row; i++)
		data[i] = new double[column];
	
	for(unsigned int i = 0; i < row; i++)
		for(unsigned int j = 0; j < column; j++)
			data[i][j] = _m[i][j];

	return (*this);
}

Matrix operator*(const Matrix& _matrix,double _value) {
	Matrix result = _matrix;
	for(unsigned int i =0; i < result.getRow(); i++) {
		for(unsigned int j = 0; j < result.getColumn(); j++) {
			result[i][j] = _matrix[i][j]*_value;
		}
	}
	return result;
}

